// Copyright Epic Games, Inc. All Rights Reserved.

#include "Character/LyraHealthComponent.h"

#include "AbilitySystem/Attributes/LyraAttributeSet.h"
#include "LyraLogChannels.h"
#include "System/LyraAssetManager.h"
#include "System/LyraGameData.h"
#include "LyraGameplayTags.h"
#include "Net/UnrealNetwork.h"
#include "GameplayEffectExtension.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "AbilitySystem/Attributes/LyraHealthSet.h"
#include "Messages/LyraVerbMessage.h"
#include "Messages/LyraVerbMessageHelpers.h"
#include "GameFramework/GameplayMessageSubsystem.h"
#include "GameFramework/PlayerState.h"
#include "Engine/World.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraHealthComponent)

UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Lyra_Elimination_Message, "Lyra.Elimination.Message"); // 定义静态游戏标签：Lyra淘汰消息

/**
 * 构造函数
 */
ULyraHealthComponent::ULyraHealthComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) // 调用父类构造函数
{
	PrimaryComponentTick.bStartWithTickEnabled = false; // 主组件Tick开始时禁用
	PrimaryComponentTick.bCanEverTick = false; // 主组件永远不能Tick

	SetIsReplicatedByDefault(true); // 默认设置为复制

	AbilitySystemComponent = nullptr; // 能力系统组件
	HealthSet = nullptr; // 健康集
	DeathState = ELyraDeathState::NotDead; // 死亡状态
}

/**
 * 获取生命周期复制属性
 */
void ULyraHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 调用父类方法

	DOREPLIFETIME(ULyraHealthComponent, DeathState); // 复制死亡状态
}

/**
 * 取消注册时调用
 */
void ULyraHealthComponent::OnUnregister()
{
	UninitializeFromAbilitySystem(); // 从能力系统取消初始化

	Super::OnUnregister(); // 调用父类方法
}

/**
 * 使用能力系统初始化
 */
void ULyraHealthComponent::InitializeWithAbilitySystem(ULyraAbilitySystemComponent* InASC)
{
	AActor* Owner = GetOwner(); // 获取所有者
	check(Owner); // 检查所有者

	if (AbilitySystemComponent) // 如果能力系统组件已存在
	{
		UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Health component for owner [%s] has already been initialized with an ability system."), *GetNameSafe(Owner)); // 记录错误日志
		return; // 返回
	}

	AbilitySystemComponent = InASC; // 设置能力系统组件
	if (!AbilitySystemComponent) // 如果能力系统组件为空
	{
		UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Cannot initialize health component for owner [%s] with NULL ability system."), *GetNameSafe(Owner)); // 记录错误日志
		return; // 返回
	}

	HealthSet = AbilitySystemComponent->GetSet<ULyraHealthSet>(); // 获取健康集
	if (!HealthSet) // 如果健康集为空
	{
		UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Cannot initialize health component for owner [%s] with NULL health set on the ability system."), *GetNameSafe(Owner)); // 记录错误日志
		return; // 返回
	}

	// 注册监听属性变化。
	HealthSet->OnHealthChanged.AddUObject(this, &ThisClass::HandleHealthChanged); // 添加健康改变委托
	HealthSet->OnMaxHealthChanged.AddUObject(this, &ThisClass::HandleMaxHealthChanged); // 添加最大健康改变委托
	HealthSet->OnOutOfHealth.AddUObject(this, &ThisClass::HandleOutOfHealth); // 添加生命值耗尽委托

	// 临时：将属性重置为默认值。最终这将由电子表格驱动。
	AbilitySystemComponent->SetNumericAttributeBase(ULyraHealthSet::GetHealthAttribute(), HealthSet->GetMaxHealth()); // 设置数值属性基础

	ClearGameplayTags(); // 清除游戏标签

	OnHealthChanged.Broadcast(this, HealthSet->GetHealth(), HealthSet->GetHealth(), nullptr); // 广播健康改变
	OnMaxHealthChanged.Broadcast(this, HealthSet->GetHealth(), HealthSet->GetHealth(), nullptr); // 广播最大健康改变
}

/**
 * 从能力系统取消初始化
 */
void ULyraHealthComponent::UninitializeFromAbilitySystem()
{
	ClearGameplayTags(); // 清除游戏标签

	if (HealthSet) // 如果健康集存在
	{
		HealthSet->OnHealthChanged.RemoveAll(this); // 移除所有健康改变委托
		HealthSet->OnMaxHealthChanged.RemoveAll(this); // 移除所有最大健康改变委托
		HealthSet->OnOutOfHealth.RemoveAll(this); // 移除所有生命值耗尽委托
	}

	HealthSet = nullptr; // 清空健康集
	AbilitySystemComponent = nullptr; // 清空能力系统组件
}

/**
 * 清除游戏标签
 */
void ULyraHealthComponent::ClearGameplayTags()
{
	if (AbilitySystemComponent) // 如果能力系统组件存在
	{
		AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dying, 0); // 设置死亡中标签计数为0
		AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dead, 0); // 设置死亡标签计数为0
	}
}

/**
 * 获取健康值
 */
float ULyraHealthComponent::GetHealth() const
{
	return (HealthSet ? HealthSet->GetHealth() : 0.0f); // 返回健康值
}

/**
 * 获取最大健康值
 */
float ULyraHealthComponent::GetMaxHealth() const
{
	return (HealthSet ? HealthSet->GetMaxHealth() : 0.0f); // 返回最大健康值
}

/**
 * 获取标准化健康值
 */
float ULyraHealthComponent::GetHealthNormalized() const
{
	if (HealthSet) // 如果健康集存在
	{
		const float Health = HealthSet->GetHealth(); // 健康值
		const float MaxHealth = HealthSet->GetMaxHealth(); // 最大健康值

		return ((MaxHealth > 0.0f) ? (Health / MaxHealth) : 0.0f); // 返回标准化健康值
	}

	return 0.0f; // 返回0
}

/**
 * 处理健康改变
 */
void ULyraHealthComponent::HandleHealthChanged(AActor* DamageInstigator, AActor* DamageCauser, const FGameplayEffectSpec* DamageEffectSpec, float DamageMagnitude, float OldValue, float NewValue)
{
	OnHealthChanged.Broadcast(this, OldValue, NewValue, DamageInstigator); // 广播健康改变
}

/**
 * 处理最大健康改变
 */
void ULyraHealthComponent::HandleMaxHealthChanged(AActor* DamageInstigator, AActor* DamageCauser, const FGameplayEffectSpec* DamageEffectSpec, float DamageMagnitude, float OldValue, float NewValue)
{
	OnMaxHealthChanged.Broadcast(this, OldValue, NewValue, DamageInstigator); // 广播最大健康改变
}

/**
 * 处理生命值耗尽
 */
void ULyraHealthComponent::HandleOutOfHealth(AActor* DamageInstigator, AActor* DamageCauser, const FGameplayEffectSpec* DamageEffectSpec, float DamageMagnitude, float OldValue, float NewValue)
{
#if WITH_SERVER_CODE // 如果有服务器代码
	if (AbilitySystemComponent && DamageEffectSpec) // 如果能力系统组件和伤害效果规范存在
	{
		// 通过所有者的能力系统发送"GameplayEvent.Death"游戏事件。这可用于触发死亡游戏能力。
		{
			FGameplayEventData Payload; // 游戏事件数据
			Payload.EventTag = LyraGameplayTags::GameplayEvent_Death; // 事件标签
			Payload.Instigator = DamageInstigator; // 发起者
			Payload.Target = AbilitySystemComponent->GetAvatarActor(); // 目标
			Payload.OptionalObject = DamageEffectSpec->Def; // 可选对象
			Payload.ContextHandle = DamageEffectSpec->GetEffectContext(); // 上下文句柄
			Payload.InstigatorTags = *DamageEffectSpec->CapturedSourceTags.GetAggregatedTags(); // 发起者标签
			Payload.TargetTags = *DamageEffectSpec->CapturedTargetTags.GetAggregatedTags(); // 目标标签
			Payload.EventMagnitude = DamageMagnitude; // 事件幅度

			FScopedPredictionWindow NewScopedWindow(AbilitySystemComponent, true); // 新范围预测窗口
			AbilitySystemComponent->HandleGameplayEvent(Payload.EventTag, &Payload); // 处理游戏事件
		}

		// 发送其他系统可以观察到的标准化动词消息
		{
			FLyraVerbMessage Message; // Lyra动词消息
			Message.Verb = TAG_Lyra_Elimination_Message; // 动词
			Message.Instigator = DamageInstigator; // 发起者
			Message.InstigatorTags = *DamageEffectSpec->CapturedSourceTags.GetAggregatedTags(); // 发起者标签
			Message.Target = ULyraVerbMessageHelpers::GetPlayerStateFromObject(AbilitySystemComponent->GetAvatarActor()); // 目标
			Message.TargetTags = *DamageEffectSpec->CapturedTargetTags.GetAggregatedTags(); // 目标标签
			//@TODO: 填写上下文标签，以及任何非能力系统源/发起者标签
			//@TODO: 确定是否是对立团队击杀、自毁、团队击杀等...

			UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld()); // 获取游戏消息子系统
			MessageSystem.BroadcastMessage(Message.Verb, Message); // 广播消息
		}

		//@TODO: 助攻消息（可以从其他地方造成的伤害计算）？
	}

#endif // #if WITH_SERVER_CODE
}

/**
 * 死亡状态复制时调用
 */
void ULyraHealthComponent::OnRep_DeathState(ELyraDeathState OldDeathState)
{
	const ELyraDeathState NewDeathState = DeathState; // 新死亡状态

	// 暂时恢复死亡状态，因为我们依赖StartDeath和FinishDeath来改变它。
	DeathState = OldDeathState; // 设置死亡状态为旧死亡状态

	if (OldDeathState > NewDeathState) // 如果旧死亡状态大于新死亡状态
	{
		// 服务器试图将我们设置回之前的状态，但我们已经预测超过了服务器状态。
		UE_LOG(LogLyra, Warning, TEXT("LyraHealthComponent: Predicted past server death state [%d] -> [%d] for owner [%s]."), (uint8)OldDeathState, (uint8)NewDeathState, *GetNameSafe(GetOwner())); // 记录警告日志
		return; // 返回
	}

	if (OldDeathState == ELyraDeathState::NotDead) // 如果旧死亡状态是未死亡
	{
		if (NewDeathState == ELyraDeathState::DeathStarted) // 如果新死亡状态是死亡开始
		{
			StartDeath(); // 开始死亡
		}
		else if (NewDeathState == ELyraDeathState::DeathFinished) // 如果新死亡状态是死亡完成
		{
			StartDeath(); // 开始死亡
			FinishDeath(); // 完成死亡
		}
		else // 否则
		{
			UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Invalid death transition [%d] -> [%d] for owner [%s]."), (uint8)OldDeathState, (uint8)NewDeathState, *GetNameSafe(GetOwner())); // 记录错误日志
		}
	}
	else if (OldDeathState == ELyraDeathState::DeathStarted) // 如果旧死亡状态是死亡开始
	{
		if (NewDeathState == ELyraDeathState::DeathFinished) // 如果新死亡状态是死亡完成
		{
			FinishDeath(); // 完成死亡
		}
		else // 否则
		{
			UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Invalid death transition [%d] -> [%d] for owner [%s]."), (uint8)OldDeathState, (uint8)NewDeathState, *GetNameSafe(GetOwner())); // 记录错误日志
		}
	}

	ensureMsgf((DeathState == NewDeathState), TEXT("LyraHealthComponent: Death transition failed [%d] -> [%d] for owner [%s]."), (uint8)OldDeathState, (uint8)NewDeathState, *GetNameSafe(GetOwner())); // 确保死亡状态等于新死亡状态
}

/**
 * 开始死亡
 */
void ULyraHealthComponent::StartDeath()
{
	if (DeathState != ELyraDeathState::NotDead) // 如果死亡状态不是未死亡
	{
		return; // 返回
	}

	DeathState = ELyraDeathState::DeathStarted; // 设置死亡状态为死亡开始

	if (AbilitySystemComponent) // 如果能力系统组件存在
	{
		AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dying, 1); // 设置死亡中标签计数为1
	}

	AActor* Owner = GetOwner(); // 获取所有者
	check(Owner); // 检查所有者

	OnDeathStarted.Broadcast(Owner); // 广播死亡开始

	Owner->ForceNetUpdate(); // 强制网络更新
}

/**
 * 完成死亡
 */
void ULyraHealthComponent::FinishDeath()
{
	if (DeathState != ELyraDeathState::DeathStarted) // 如果死亡状态不是死亡开始
	{
		return; // 返回
	}

	DeathState = ELyraDeathState::DeathFinished; // 设置死亡状态为死亡完成

	if (AbilitySystemComponent) // 如果能力系统组件存在
	{
		AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dead, 1); // 设置死亡标签计数为1
	}

	AActor* Owner = GetOwner(); // 获取所有者
	check(Owner); // 检查所有者

	OnDeathFinished.Broadcast(Owner); // 广播死亡完成

	Owner->ForceNetUpdate(); // 强制网络更新
}

/**
 * 伤害自毁
 */
void ULyraHealthComponent::DamageSelfDestruct(bool bFellOutOfWorld)
{
	if ((DeathState == ELyraDeathState::NotDead) && AbilitySystemComponent) // 如果死亡状态是未死亡且能力系统组件存在
	{
		const TSubclassOf<UGameplayEffect> DamageGE = ULyraAssetManager::GetSubclass(ULyraGameData::Get().DamageGameplayEffect_SetByCaller); // 伤害游戏效果类
		if (!DamageGE) // 如果伤害游戏效果类不存在
		{
			UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: DamageSelfDestruct failed for owner [%s]. Unable to find gameplay effect [%s]."), *GetNameSafe(GetOwner()), *ULyraGameData::Get().DamageGameplayEffect_SetByCaller.GetAssetName()); // 记录错误日志
			return; // 返回
		}

		FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(DamageGE, 1.0f, AbilitySystemComponent->MakeEffectContext()); // 创建传出规范句柄
		FGameplayEffectSpec* Spec = SpecHandle.Data.Get(); // 获取游戏效果规范

		if (!Spec) // 如果游戏效果规范不存在
		{
			UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: DamageSelfDestruct failed for owner [%s]. Unable to make outgoing spec for [%s]."), *GetNameSafe(GetOwner()), *GetNameSafe(DamageGE)); // 记录错误日志
			return; // 返回
		}

		Spec->AddDynamicAssetTag(TAG_Gameplay_DamageSelfDestruct); // 添加动态资产标签：伤害自毁

		if (bFellOutOfWorld) // 如果掉出世界
		{
			Spec->AddDynamicAssetTag(TAG_Gameplay_FellOutOfWorld); // 添加动态资产标签：掉出世界
		}

		const float DamageAmount = GetMaxHealth(); // 伤害量

		Spec->SetSetByCallerMagnitude(LyraGameplayTags::SetByCaller_Damage, DamageAmount); // 设置调用者幅度
		AbilitySystemComponent->ApplyGameplayEffectSpecToSelf(*Spec); // 应用游戏效果规范到自身
	}
}